/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.fyx.linker.web.category;

import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.PostConstruct;
import javax.ejb.EJB;
import javax.faces.application.FacesMessage;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import org.fyx.core.PermissionDeniedException;
import org.fyx.linker.LinkerConstants;
import org.fyx.linker.LinkerRoles;
import org.fyx.linker.controll.LinkerCategoryFacadeLocal;
import org.fyx.linker.controll.LinkerLinkFacadeLocal;
import org.fyx.linker.entity.LinkerCategory;
import org.fyx.linker.entity.LinkerLink;
import org.fyx.linker.web.LinkerLocal;
import org.fyx.mula.ejb.MulaLocal;
import org.fyx.util.FyxUtility;
import org.fyx.web.context.WebContext;
import org.fyx.web.context.WebPage;
import org.primefaces.component.menuitem.MenuItem;
import org.primefaces.model.DualListModel;
import org.primefaces.model.TreeNode;

/**
 * Managed bean pro editaci odkazu.
 * Zodpovida za spravne prevedeni databazoveho stromu na model stromu primeface,
 * jeho modifikaci a zpetny prevod na model databaze.
 * 
 * @author xljan
 */
@ManagedBean
@SessionScoped
public class LinkerEdit implements Serializable {

    @EJB
    private LinkerCategoryFacadeLocal linkerCategoryFacade;
    @EJB
    private LinkerLinkFacadeLocal linkerLinkFacade;
    @EJB
    private MulaLocal mula;
    @EJB
    private LinkerLocal linker;
    // Superkoren, je predan pro primfaces tree
    private CategoryTreeNode rootRoot;
    // Koren viditelneho stromu uzivateli
    private CategoryTreeNode root;
    // Aktualne vybrany node
    private CategoryTreeNode selectedNode;
    // Jmeno nove vytvarene kategorie
    private String categoryName;
    // Mapa polozek v menu, kde klic je unikatni Id
    private Map<String, MenuItem> menuItems;
    // Model stromu
    private DualListModel<MenuItem> dualListModel;

    @PostConstruct
    public void postConstruct() {
        FyxUtility.checkRole(LinkerRoles.CATEGORY_MANAGER);
        init();
    }

    public void init() {
        root = null;
        rootRoot = null;
        categoryName = "";
        menuItems = new HashMap<String, MenuItem>();
        dualListModel = new DualListModel<MenuItem>();
        menuItems = new HashMap<String, MenuItem>(WebContext.getMapAllMenuItems());
        importModel();
        dualListModel.getSource().addAll(menuItems.values());
        selectedNode = root;
        root.setExpanded(true);
        root.setSelected(true);
    }

    // Getters and setters
    public CategoryTreeNode getRoot() {
        return rootRoot;
    }

    public LinkerCategory getDetailCategory() {
        if (selectedNode == null) {
            return null;
        }
        return selectedNode.getCategory();
    }

    public CategoryTreeNode getSelectedNode() {
        return selectedNode;
    }

    public void setSelectedNode(CategoryTreeNode selectedNode) {
        this.selectedNode = selectedNode;
        onNodeSelect();
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public DualListModel<MenuItem> getDualListModel() {
        return dualListModel;
    }

    public void setDualListModel(DualListModel<MenuItem> dualListModel) {
        this.dualListModel = dualListModel;
    }

    /**
     * Vrati pravocni kategorii.
     * Pokud je oznacena kategorie v {@link LinkerEdit#selectedNode}, vrati ji,
     * jinak vrati koren.
     * 
     * @return pracovni kategorii
     */
    public CategoryTreeNode getWorkingCategory() {
        if (selectedNode == null) {
            return root;
        }
        return selectedNode;
    }

    public List<LinkerCategory> getAllCategory() {
        return linkerCategoryFacade.findAll();
    }

    public List<LinkerLink> getAllLinks() {
        return linkerLinkFacade.findAll();
    }

    // Methods
    public void checkRoles() {
        if (!WebContext.getCurrentContext().
                isUserInRole(LinkerRoles.CATEGORY_MANAGER)) {
            throw new PermissionDeniedException();
        }
    }

    /**
     * Pouzije aktualni nastaveni komponenty PickList 
     * a ulozi jej do vybraneho node.
     */
    public void applyCurentCategory() {
        if (sendSelectedNodeRequired()) {
            return;
        }
        selectedNode.getMenuItems().clear();
        selectedNode.getMenuItems().addAll(dualListModel.getTarget());
        WebPage.sendMessage(FacesMessage.SEVERITY_INFO, mula.get("Apply sucefull"));
        selectedNode.setSelected(true);
    }

    /**
     * Funkce se spousti po vybrani nodu uzivatelem.
     */
    public void onNodeSelect() {
        dualListModel.getTarget().clear();
        dualListModel.getTarget().addAll(getWorkingCategory().getMenuItems());
    }

    /**
     * Ulozi kompletni model do databaze.
     */
    public void save() {
        linker.updateMenu(root);
        linker.recreateMenuModel();
        WebContext.getMessages().add(new FacesMessage(FacesMessage.SEVERITY_INFO,
                mula.get("Save sucefull"), ""));
        WebPage.refresh();
    }

    /**
     * Prida novou podkategorii.
     */
    public void addSubcategory() {
        if (sendSelectedNodeRequired()
                || sendAlredyContainsName(selectedNode, categoryName)) {
            return;
        }
        if (containsName(selectedNode, categoryName)) {
            return;
        }
        LinkerCategory category = new LinkerCategory();
        category.setName(categoryName);
        selectedNode.setExpanded(true);
        selectedNode.setSelected(false);
        new CategoryTreeNode(category, selectedNode);
        selectedNode.setSelected(false);
        selectedNode = null;
    }

    /**
     * Jestlize node ma jiz potomka se stejnym jmenem kategorie, 
     * odesle zpravu uzivateli a vrati true, jinak false.
     * @param node uzel
     * @param categoryName jmeno kategorie
     * @return true
     */
    private boolean sendAlredyContainsName(CategoryTreeNode node, String categoryName) {
        if (containsName(node, categoryName)) {
            WebPage.sendMessage(FacesMessage.SEVERITY_ERROR,
                    mula.get("Category's name alredy exist"));
            return true;
        }
        return false;
    }

    /**
     * Pokud node obsahuje potomka s atributem name shodnym s categoryName
     * vrati true, jinak false
     * @param node uzel
     * @param categoryName
     * @return true
     */
    private boolean containsName(CategoryTreeNode node, String categoryName) {
        for (TreeNode child : node.getChildren()) {
            if (((CategoryTreeNode) child).getCategory().getName().
                    equals(categoryName)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Odebere node a vsechny jeho potomky ze stromove struktury.
     * Pokud neni vybran prislusny node, zasle o tom zpravu klientovi.
     * Pokud je pozadavek na odebrani roota, zasle zpravu o nemoznosti odebrani.
     */
    public void remove() {
        if (sendSelectedNodeRequired()) {
            return;
        }
        if (selectedNode == root) {
            WebPage.sendMessage(FacesMessage.SEVERITY_ERROR,
                    mula.get("Main category is not possible deleted."));
            return;
        }
        dualListModel.getSource().addAll(selectedNode.removeCascade());
        selectedNode.setSelected(false);
        selectedNode = null;
    }

    /**
     * Vytvori stromovou strukturu uzlu TreeNode a jeji koren
     * priradi do promenne this.root.
     * Pokud v databazi neni nalezen koren, vytvori novy standardni koren.
     */
    private void importModel() {
        // Vytvori superroota, ktery ve vystupnim stromu neni zobrazen
        // a odvijji se od nej strom
        rootRoot = new CategoryTreeNode("super_root", null);

        LinkerCategory rootCategory = linkerCategoryFacade.findRoot();
        if (rootCategory == null) {
            // Pokud se v databazi nenaleza koren, vytvori se novy
            rootCategory = new LinkerCategory();
            rootCategory.setName(LinkerConstants.ROOT_CATEGORY_NAME);
        }
        root = new CategoryTreeNode(rootCategory, rootRoot);
        importModel(root);
    }

    /**
     * Rekurzivni funkce pro nacitani stromoveho modelu uzlu TreeNode
     * z databaze.
     * @param node pocatek nacitani
     */
    private void importModel(CategoryTreeNode node) {
        pairMenuItemByLink(node);
        for (LinkerCategory child : node.getCategory().getLinkerCategoryCollection()) {
            importModel(new CategoryTreeNode(child, node));
        }
    }

    /**
     * Sparuje dostupne odkazy s odkazy definovanymi ke kategorii.
     * @param ctn 
     */
    private void pairMenuItemByLink(CategoryTreeNode ctn) {
        for (LinkerLink link : ctn.getCategory().getLinkerLinkCollection()) {
            MenuItem pair = menuItems.remove(link.getIdLink());
            if (pair != null) {
                ctn.getMenuItems().add(pair);
            }
        }
    }

    /**
     * Pokud na vystupnim stromu neni vybran zadny node, vrati false a odesle
     * klientovi zpravu.
     * @return zprava byla zaslana
     */
    public boolean sendSelectedNodeRequired() {
        if (selectedNode == null) {
            WebPage.sendMessage(FacesMessage.SEVERITY_WARN, mula.get("Please select category"));
            return true;
        }
        return false;
    }
}
